﻿using JGSY.SmartIndustry.Dashboard.Core;
using JGSY.SmartIndustry.Dashboard.Models;
using LiveCharts;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO.Ports;
using System.Management;
using System.Text;
using System.Windows.Threading;

namespace JGSY.SmartIndustry.Dashboard.ViewModels
{
   public class MainViewModel : NotifyBase
    {
        #region 下拉列表集合定义
        public List<string> PortList { get; set; }
        public List<int> BaudRateList { get; set; } = new List<int> {
            4800,
            9600,
            14400,
            19200,
            38400,
            57600,
            115200,
            128000
        };
        public List<int> DataBitList { get; set; } = new List<int>
        {
            5,
            7,
            8,
        };
        public List<string> StopBitList { get; set; }
        public List<string> ParityList { get; set; }
        #endregion

        #region 消息提醒
        private string _message = "连接未启动";

        public string Message
        {
            get { return _message; }
            set
            {
                SetProperty<string>(ref _message, value);
            }
        }
        private string _messageColor = "Green";

        public string MessageColor
        {
            get { return _messageColor; }
            set { SetProperty<string>(ref _messageColor, value); }
        }

        public MessageModel MessageInfo { get; set; } = new MessageModel();
        #endregion

        #region 下拉集合是否可用
        private bool _enabled = true;

        public bool Enabled
        {
            get { return _enabled; }
            set { SetProperty<bool>(ref _enabled, value); }
        }
        #endregion


        public PortPropertyModel PropertyModel { get; set; } = new PortPropertyModel();

        public DeviceModel DeviceModel { get; set; } = new DeviceModel();

        public List<LightModel> LightList { get; set; } = new List<LightModel>();

        private bool _startState;

        public bool StartState
        {
            get { return _startState; }
            set
            {
                bool oldValue = _startState;
                SetProperty<bool>(ref _startState, value);
                try
                {
                    DoStart();
                }
                catch (Exception ex)
                {
                    _startState = oldValue;
                    this.ShowMessage(ex.Message, "通信异常", "Orange");
                    this.RaisePropertyChanged(nameof(StartState));
                }
            }
        }

        private bool _allState;

        public bool AllState
        {
            get { return _allState; }
            set { SetProperty<bool>(ref _allState, value); }
        }

        private DateTime _currentTime = DateTime.Now;

        public DateTime CurrentTime
        {
            get { return _currentTime; }
            set { SetProperty<DateTime>(ref _currentTime, value); }
        }


        public string SendText { get; set; } = "Hello Zhaoxi";
        private string _oledText;

        public string OledText
        {
            get { return _oledText; }
            set { SetProperty<string>(ref _oledText, value); }
        }

        private double currentCpu;

        public double CurrentCPU
        {
            get { return currentCpu; }
            set
            {
                SetProperty(ref currentCpu, Math.Ceiling(value));
            }
        }

        private double currnetMem;

        public double CurrentMem
        {
            get { return currnetMem; }
            set
            {
                SetProperty(ref currnetMem, Math.Ceiling(value));
            }
        }
        private PerformanceCounter cpuCounter;
        private ManagementClass memCounter;

        public ChartValues<double> TemperatureValues { get; set; } = new ChartValues<double>() {
            38,70,57,62,67,27,75,56,79,20,77,46,33,63,49,56,79,20,77,46
        };
        public ChartValues<double> HumidityValues { get; set; } = new ChartValues<double>() {
            46,20,30,56,57,33,76,54,74,65,66,24,71,77,58,20,30,56,57,33
        };
        public ChartValues<double> BrightnessValues { get; set; } = new ChartValues<double>() {
            56,40,20,86,17,33,56,34,74,95,16,44,11,97,18,86,17,33,56,34
        };
        public ObservableCollection<string> XLabels { get; set; } = new ObservableCollection<string>();

        public ObservableCollection<LogModel> LogList { get; set; } = new ObservableCollection<LogModel>();

        public Command LightCommand { get; set; }
        public Command TextCommand { get; set; }
        public Command ResendCommand { get; set; }


        DispatcherTimer timer = new DispatcherTimer();
        SerialPort serialPort;
        public MainViewModel()
        {
            serialPort = new SerialPort();

            cpuCounter = new PerformanceCounter();
            cpuCounter.CategoryName = "Processor";
            cpuCounter.CounterName = "% Processor Time";
            cpuCounter.InstanceName = "_Total";
            //cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");

            memCounter = new ManagementClass();



            StopBitList = Enum.GetNames(typeof(StopBits)).ToList();
            PropertyModel.StopBits = "One";

            ParityList = Enum.GetNames(typeof(Parity)).ToList();
            PropertyModel.Parity = ParityList[0];


            LightList.Add(new LightModel { Address = 0 });
            LightList.Add(new LightModel { Address = 1 });
            LightList.Add(new LightModel { Address = 2 });
            LightList.Add(new LightModel { Address = 3 });
            LightList.Add(new LightModel { Address = 4 });

            LightCommand = new Command(OnLight);
            TextCommand = new Command(OnTextSend);
            ResendCommand = new Command(OnResend);

            var time = DateTime.Now;
            for (int i = 19; i >= 0; i--)
            {
                XLabels.Add(time.AddSeconds(i * -1).ToString("ss"));
            }

            for (int i = 0; i < 10; i++)
            {
                LogList.Add(new LogModel
                {
                    LogInfo = $"Hello ZhaoxiEDU.net-[{i.ToString("00")}]",
                });
            }

            timer.Interval = new TimeSpan(0, 0, 1);
            timer.Tick += (se, ev) =>
            {
                CurrentTime = DateTime.Now;

                if (PortList == null)
                {
                    PortList = SerialPort.GetPortNames().ToList();
                    if (PortList.Count > 0)
                    {
                        PropertyModel.PortName = PortList[0];
                    }
                    this.RaisePropertyChanged(nameof(PortList));
                }
                else if (!PortList.SequenceEqual(SerialPort.GetPortNames()))
                {
                    PortList = SerialPort.GetPortNames().ToList();
                    if (!PortList.Exists(p => p == PropertyModel.PortName))
                    {
                        PropertyModel.PortName = "";
                        if (PortList.Count > 0)
                        {
                            PropertyModel.PortName = PortList[0];
                        }
                    }
                    this.RaisePropertyChanged(nameof(PortList));
                }
            };
            timer.Start();

            Task.Run(async () =>
            {
                while (true)
                {
                    this.CurrentCPU = this.GetCPUInfo();
                    this.CurrentMem = this.GetMemInfo();
                    await Task.Delay(1000);
                }
            });
        }

        private void OnTextSend(object? obj)
        {
            if (!serialPort.IsOpen) return;

            try
            {
                if (string.IsNullOrEmpty(this.SendText))
                    throw new Exception("请输入有效英文字符");
                if (this.SendText.Length > 60)
                    throw new Exception("输入的字符长度不能超过60");
                if (this.SendText.ToList().Exists(s => (int)s > 127))
                    throw new Exception("包含无效字符");

                SendOledText(this.SendText);
            }
            catch (Exception ex)
            {
                this.ShowMessage(ex.Message, "通信异常", "Orange");
            }
        }

        private void OnResend(object? obj)
        {
            if (!serialPort.IsOpen) return;

            try
            {
                string text = (obj as LogModel).LogInfo;
                SendOledText(text);
            }
            catch (Exception ex)
            {
                this.ShowMessage(ex.Message, "通信异常", "Orange");
            }
        }

        private void SendOledText(string text)
        {
            byte[] text_bytes = Encoding.ASCII.GetBytes(text);
            List<byte> byteList = new List<byte>();
            byteList.Add(0x01);
            byteList.Add(0x10);
            byteList.Add(0x00);
            byteList.Add(0x00);
            byteList.Add((byte)(Math.Ceiling(text_bytes.Length * 1.0 / 2) / 256));
            byteList.Add((byte)(Math.Ceiling(text_bytes.Length * 1.0 / 2) % 256));
            byteList.Add((byte)text_bytes.Length);
            byteList.AddRange(text_bytes);
            if (text_bytes.Length % 2 == 1)
                byteList.Add(0x00);

            byteList.Add(0x00);
            byteList.Add(0x00);

            byte[] bytes = byteList.ToArray();
            CRC16(bytes);

            SendAndRecieve(bytes);

            this.LogList.Insert(0, new LogModel { LogInfo = text });
            if (this.LogList.Count > 40)
                this.LogList.RemoveAt(this.LogList.Count - 1);

            this.OledText = text;
        }

        bool isWriting = false;
        private void OnLight(object? param)
        {
            if (!serialPort.IsOpen) return;

            try
            {
                if (param.ToString() == "all")
                {
                    // 0001 1111
                    byte light1 = (byte)(AllState ? 0x01 : 0x00);
                    byte light2 = (byte)((AllState ? 0x01 : 0x00) << 1);
                    byte light3 = (byte)((AllState ? 0x01 : 0x00) << 2);
                    byte light4 = (byte)((AllState ? 0x01 : 0x00) << 3);
                    byte light5 = (byte)((AllState ? 0x01 : 0x00) << 4);
                    byte[] bytes = new byte[] {
                        0x01,// 从站地址
                        0x0F,// 功能码    15  写从个线圈状态
                        0x00,
                        0x00,// 起始地址
                        0x00,
                        0x05,// 写多少个
                        0x01,// 写入的数量   字节数
                        (byte)(0x00|light1|light2|light3|light4|light5), // 状态数据
                        0x00,0x00 // 校验码
                    };
                    CRC16(bytes);
                    this.SendAndRecieve(bytes);

                    LightList.ForEach(l => l.State = AllState);
                }
                else
                {
                    isWriting = true;
                    ushort index = ushort.Parse(param.ToString());
                    var value = LightList[index].State;

                    //byte light1 = (byte)(value ? 0x01 : 0x00);

                    AllState = !LightList.Exists(l => !l.State);
                    byte[] bytes = new byte[] {
                        0x01,// 从站地址
                        0x05,// 单写功能码    
                        (byte)(index/256),
                        (byte)(index%256),// 地址
                        (byte)(value?0xFF: 0x00),
                        0x00,
                        0x00,0x00// 校验码
                    };
                    CRC16(bytes);
                    this.SendAndRecieve(bytes);

                    isWriting = false;
                }
            }
            catch (Exception ex)
            {
                this.ShowMessage(ex.Message, "通信异常", "Orange");
                isWriting = false;
            }
        }

        private void DoStart()
        {
            if (serialPort.IsOpen)
            {
                serialPort.Close();

                this.ShowMessage("连接已断开，等待连接设备...");
                Enabled = true;
            }
            else
            {
                if (string.IsNullOrEmpty(PropertyModel.PortName))
                    throw new Exception("请先选择串口");

                serialPort.PortName = PropertyModel.PortName;
                serialPort.BaudRate = PropertyModel.BaudRate;
                serialPort.Parity = (Parity)Enum.Parse(typeof(Parity), PropertyModel.Parity);
                serialPort.DataBits = PropertyModel.DataBits;
                serialPort.StopBits = (StopBits)Enum.Parse(typeof(StopBits), PropertyModel.StopBits);

                serialPort.Open();

                Task.Run(async () =>
                {
                    while (serialPort.IsOpen)
                    {
                        this.OnMonitor();
                        await Task.Delay(1000);
                    }
                });

                this.ShowMessage("连接正常！正在监视接收学习卡数据");
                Enabled = false;
            }
        }

        int[] lastRandom = new int[3];
        bool[] order = new bool[] { true, true, true };
        Random random = new Random();
        private void OnMonitor()
        {
            try
            {
                // 请示温湿度与亮度
                byte[] bytes = new byte[8];
                bytes[0] = 0x01;// 从站地址
                bytes[1] = 0x04;// 功能码  读保持型寄存器       01  读线圈状态  （输入寄存器   输入线圈  只读）
                bytes[2] = 0x00;
                bytes[3] = 0x00;// 起始地址    0-255     0-65535
                bytes[4] = 0x00;
                bytes[5] = 0x03;// 读取数量
                CRC16(bytes);

                byte[] resp = SendAndRecieve(bytes);


                if (resp != null && resp.Length > 0 && resp[1] == 0x04)
                {
                    if (DeviceModel.UseSimulationTemp)
                    {
                        GenarateRandom(ref lastRandom[0], ref order[0], (int)DeviceModel.MinSimValueTemp, (int)DeviceModel.MaxSimValueTemp);
                        DeviceModel.Temperature = lastRandom[0];
                    }
                    else
                        DeviceModel.Temperature = BitConverter.ToInt16(new byte[] { resp[4], resp[3] }, 0) * 0.1f;// 字节序

                    if (DeviceModel.UseSimulationHumi)
                    {
                        GenarateRandom(ref lastRandom[1], ref order[1], (int)DeviceModel.MinSimValueHumi, (int)DeviceModel.MaxSimValueHumi);
                        DeviceModel.Humidity = (int)lastRandom[1];
                    }
                    else
                        DeviceModel.Humidity = BitConverter.ToUInt16(new byte[] { resp[6], resp[5] }, 0);

                    if (DeviceModel.UseSimulationBright)
                    {
                        GenarateRandom(ref lastRandom[2], ref order[2], (int)DeviceModel.MinSimValueBright, (int)DeviceModel.MaxSimValueBright);
                        DeviceModel.Brightness = (int)lastRandom[2];
                    }
                    else
                        DeviceModel.Brightness = BitConverter.ToUInt16(new byte[] { resp[8], resp[7] }, 0);

                    TemperatureValues.Add(DeviceModel.Temperature);
                    HumidityValues.Add(DeviceModel.Humidity);
                    BrightnessValues.Add(DeviceModel.Brightness);
                    XLabels.Add(DateTime.Now.ToString("ss"));

                    if (XLabels.Count > 20)
                    {
                        TemperatureValues.RemoveAt(0);
                        HumidityValues.RemoveAt(0);
                        BrightnessValues.RemoveAt(0);
                        XLabels.RemoveAt(0);
                    }
                }

                // 灯珠状态
                bytes = new byte[] {
                    0x01,// 从站地址
                    0x01,// 功能码
                    0x00,
                    0x00,// 起始地址
                    0x00,
                    0x05,// 读取数量
                    0x00,
                    0x00 // 校验码
                };
                CRC16(bytes);
                resp = this.SendAndRecieve(bytes);

                if (resp.Length > 0 && resp[1] < 0x80 && !isWriting)
                {
                    LightList[0].State = (resp[3] & 1) == 1;
                    LightList[1].State = (resp[3] & 2) == 2;
                    LightList[2].State = (resp[3] & 4) == 4;
                    LightList[3].State = (resp[3] & 8) == 8;
                    LightList[4].State = (resp[3] & 16) == 16;
                }

            }
            catch (Exception ex)
            {
                this.ShowMessage(ex.Message, "通信异常", "Orange");
            }
        }

        private void GenarateRandom(ref int randomValue, ref bool order, int min, int max)
        {
            if (randomValue == 0)
                randomValue = random.Next(Math.Min(min, max), Math.Max(min, max));
            else
            {
                //if (randomValue > max - 5)
                //    order = false;
                //else if (randomValue < min + 5)
                //    order = false;

                int rv = randomValue;
                if (order)
                {
                    var _max = Math.Min(rv + 20, max);

                    randomValue = random.Next(rv - 5, _max - 1);
                    Debug.WriteLine($"{randomValue} - {rv} - {_max} - {max}");

                    if (_max == max)
                        order = false;
                }
                else
                {
                    var _min = Math.Max(rv - 20, min);

                    randomValue = random.Next(_min + 1, rv + 5);
                    Debug.WriteLine($"{randomValue} - {rv} - {_min} - {min}");

                    if (_min == min)
                        order = true;
                }
            }
        }

        private static readonly object _lockObject = new object();
        private byte[] SendAndRecieve(byte[] bytes)
        {
            lock (_lockObject)
            {
                serialPort.Write(bytes, 0, bytes.Length);

                Task.Delay(100).Wait();
                byte[] resp = new byte[serialPort.BytesToRead];
                serialPort.Read(resp, 0, resp.Length);

                return resp;
            }
        }

        void CRC16(byte[] value)
        {
            if (value == null || !value.Any())
                throw new ArgumentException("");

            //运算
            ushort crc = 0xFFFF;
            for (int i = 0; i < value.Length - 2; i++)
            {
                crc = (ushort)(crc ^ (value[i]));
                for (int j = 0; j < 8; j++)
                {
                    crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1);
                }
            }
            byte hi = (byte)((crc & 0xFF00) >> 8);  //高位置
            byte lo = (byte)(crc & 0x00FF);         //低位置


            value[value.Length - 2] = lo;
            value[value.Length - 1] = hi;
        }

        private void ShowMessage(string message, string title = "运行提示", string color = "#00CFF8")
        {
            MessageInfo.Message = message;
            MessageInfo.MainColor = color;
            MessageInfo.Title = title;
            MessageInfo.MsgTime = DateTime.Now;
        }

        public double GetMemInfo()
        {
            memCounter.Path = new ManagementPath("Win32_PhysicalMemory");
            ManagementObjectCollection moc = memCounter.GetInstances();
            double available = 0, capacity = 0;

            foreach (ManagementObject mo1 in moc)
            {
                capacity += ((Math.Round(Int64.Parse(mo1.Properties["Capacity"].Value.ToString()) / 1024 / 1024 / 1024.0, 1)));
            }
            moc.Dispose();

            memCounter.Path = new ManagementPath("Win32_PerfFormattedData_PerfOS_Memory");
            moc = memCounter.GetInstances();
            foreach (ManagementObject mo2 in moc)
            {
                available += ((Math.Round(Int64.Parse(mo2.Properties["AvailableMBytes"].Value.ToString()) / 1024.0, 1)));
            }
            moc.Dispose();

            return (capacity - available) / capacity * 100;
        }

        public double GetCPUInfo()
        {
            return this.cpuCounter.NextValue();
        }
    }
}
